using System;
using System.Data;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Dapper.ExpressionExtension.Resolvers;
using Dapper.ExpressionExtension.SqlBuilders;

namespace Dapper.ExpressionExtension.ExpressionMappers
{
    public static partial class ExpressionMapper
    {
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="connection"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        public static long Count<TEntity>(this IDbConnection connection, IDbTransaction transaction = null)
        {
            var sql = BuildCountAllSql(GetSqlBuilder(connection), typeof(TEntity));
            LogQuery<TEntity>(sql);
            return connection.ExecuteScalar<long>(sql, transaction);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="connection"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        public static Task<long> CountAsync<TEntity>(this IDbConnection connection, IDbTransaction transaction = null)
        {
            var sql = BuildCountAllSql(GetSqlBuilder(connection), typeof(TEntity));
            LogQuery<TEntity>(sql);
            return connection.ExecuteScalarAsync<long>(sql, transaction);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="connection"></param>
        /// <param name="predicate"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        public static long Count<TEntity>(this IDbConnection connection, Expression<Func<TEntity, bool>> predicate, IDbTransaction transaction = null)
        {
            var sql = BuildCountSql(GetSqlBuilder(connection), predicate, out var parameters);
            LogQuery<TEntity>(sql);
            return connection.ExecuteScalar<long>(sql, parameters, transaction);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="connection"></param>
        /// <param name="predicate"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        public static Task<long> CountAsync<TEntity>(this IDbConnection connection, Expression<Func<TEntity, bool>> predicate, IDbTransaction transaction = null)
        {
            var sql = BuildCountSql(GetSqlBuilder(connection), predicate, out var parameters);
            LogQuery<TEntity>(sql);
            return connection.ExecuteScalarAsync<long>(sql, parameters, transaction);
        }

        internal static string BuildCountAllSql(ISqlBuilder sqlBuilder, Type type)
        {
            var cacheKey = new QueryCacheKey(QueryCacheType.Count, sqlBuilder, type);
            if (!QueryCache.TryGetValue(cacheKey, out var sql))
            {
                var tableName = ResolverHelper.Table(type, sqlBuilder);
                sql = $"select count(*) from {tableName}";
                QueryCache.TryAdd(cacheKey, sql);
            }

            return sql;
        }

        internal static string BuildCountSql<TEntity>(ISqlBuilder sqlBuilder, Expression<Func<TEntity, bool>> predicate, out DynamicParameters parameters)
        {
            var sql = BuildCountAllSql(sqlBuilder, typeof(TEntity));
            sql += CreateSqlExpression<TEntity>(sqlBuilder)
                .Where(predicate)
                .ToSql(out parameters);
            return sql;
        }
    }
}
